package me.cxlr.qinlauncher2.manager;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;

import com.orhanobut.logger.Logger;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Random;

import lombok.Getter;
import lombok.Setter;
import me.cxlr.qinlauncher2.MyApplication;
import me.cxlr.qinlauncher2.constants.Constants;
import me.cxlr.qinlauncher2.util.SharedPreferencesUtil;

/**
 * 图标包管理
 *
 * @author 酷安用户:山轮纸
 */
public class IconManager {
    private volatile static IconManager iconManager;

    private IconManager() {
        this.initIconPackage();
    }

    public static IconManager getInstance() {
        if (iconManager == null) {
            synchronized (IconManager.class) {
                if (iconManager == null) {
                    iconManager = new IconManager();
                }
            }
        }
        return iconManager;
    }

    /**
     * 获取App图标
     *
     * @param packageName 包名
     * @param className   入口Activity名
     * @return 图标
     */
    public Drawable getApplicationIcon(String packageName, String className) {
        PackageManager packageManager = SoftwareManager.getInstance().getPackageManager();

        Intent intent = new Intent().addCategory(Intent.CATEGORY_LAUNCHER).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setPackage(packageName);
        intent.setClassName(packageName, className);
        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
        if (resolveInfoList.size() > 0) {
            ResolveInfo resolveInfo = resolveInfoList.get(0);
            ActivityInfo activityInfo = resolveInfo.activityInfo;
            if (defaultIconPack != null) {
                return defaultIconPack.getDrawableIconForPackage(packageName, activityInfo.loadIcon(packageManager));
            } else {
                return activityInfo.loadIcon(packageManager);
            }
        } else {
            return null;
        }
    }

    /**
     * 图标包加载
     */
    private void initIconPackage() {
        PackageManager pm = MyApplication.getInstance().getPackageManager();

        List<ResolveInfo> resolveInfoList = new ArrayList<>(16);
        HashMap<String, ResolveInfo> resolveInfoHashMap = new HashMap<>(16);
        List<ResolveInfo> resolveInfoList1 = pm.queryIntentActivities(new Intent("com.novalauncher.THEME"), PackageManager.GET_META_DATA);
        if (resolveInfoList1 != null) {
            for (ResolveInfo resolveInfo : resolveInfoList1) {
                resolveInfoHashMap.put(resolveInfo.activityInfo.packageName, resolveInfo);
            }
        }
        List<ResolveInfo> resolveInfoList2 = pm.queryIntentActivities(new Intent("com.gau.go.launcherex.theme"), PackageManager.GET_META_DATA);
        if (resolveInfoList2 != null) {
            for (ResolveInfo resolveInfo : resolveInfoList2) {
                resolveInfoHashMap.put(resolveInfo.activityInfo.packageName, resolveInfo);
            }
        }
        List<ResolveInfo> resolveInfoList3 = pm.queryIntentActivities(new Intent("org.adw.launcher.THEMES"), PackageManager.GET_META_DATA);
        if (resolveInfoList3 != null) {
            for (ResolveInfo resolveInfo : resolveInfoList3) {
                resolveInfoHashMap.put(resolveInfo.activityInfo.packageName, resolveInfo);
            }
        }
        if (resolveInfoHashMap.size() > 0) {
            for (String key : resolveInfoHashMap.keySet()) {
                resolveInfoList.add(resolveInfoHashMap.get(key));
            }
        }

        String defIconPackage = SharedPreferencesUtil.getInstance().getString("select_icon_package", "system");
        // 加载系统图标
        if ("system".equals(defIconPackage)) {
            defaultIconPack = new IconPack();
            defaultIconPack.iconPackPackageName = "system";
            defaultIconPack.iconPackLabel = "系统默认";
            Logger.v("加载系统图标");
        }
        // 加载其他图标包
        else {
            // 有图标包软件
            if (resolveInfoList.size() > 0) {
                for (ResolveInfo resolveInfo : resolveInfoList) {
                    try {
                        String packageName = resolveInfo.activityInfo.packageName;
                        String label = MyApplication.getInstance().getPackageManager().getApplicationLabel(resolveInfo.activityInfo.applicationInfo).toString();
                        // 有用户选择的图标包软件
                        if (packageName.equals(defIconPackage)) {
                            defaultIconPack = new IconPack();
                            defaultIconPack.iconPackPackageName = packageName;
                            defaultIconPack.iconPackLabel = label;
                            defaultIconPack.load();
                            Logger.v("加载图标包");
                            break;
                        }
                    } catch (Exception e) {
                        defaultIconPack = new IconPack();
                        defaultIconPack.iconPackPackageName = "system";
                        defaultIconPack.iconPackLabel = "系统默认";
                        Logger.v(e.toString());
                    }
                }
            }
        }
    }

    /**
     * 重选图标包后加载
     */
    public void reloadIconPackage() {
        initIconPackage();
    }

    private IconPack defaultIconPack;

    public static class IconPack {
        public String iconPackPackageName;
        public String iconPackLabel;

        private boolean flagLoaded = false;
        private HashMap<String, String> packagesDrawablesMap = new HashMap<>(16);

        private List<Bitmap> backImageList = new ArrayList<>(16);
        private Bitmap bitmapMaskImage = null;
        private Bitmap bitmapFrontImage = null;
        private float mFactor = 0.8F;

        Resources resourcesIconPacks = null;

        /**
         * 加载图标包的资源
         */
        public void load() {
            PackageManager packageManager = SoftwareManager.getInstance().getPackageManager();
            try {
                XmlPullParser xmlPullParser = null;

                resourcesIconPacks = packageManager.getResourcesForApplication(iconPackPackageName);
                int appFilterId = resourcesIconPacks.getIdentifier("appfilter", "xml", iconPackPackageName);
                if (appFilterId > 0) {
                    xmlPullParser = resourcesIconPacks.getXml(appFilterId);
                } else {
                    try {
                        InputStream appFilterStream = resourcesIconPacks.getAssets().open("appfilter.xml");
                        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
                        factory.setNamespaceAware(true);
                        xmlPullParser = factory.newPullParser();
                        xmlPullParser.setInput(appFilterStream, "utf-8");
                    } catch (IOException e) {
                        Logger.e("没有 appfilter.xml 文件:" + e.toString());
                    }
                }
                if (xmlPullParser != null) {
                    int eventType = xmlPullParser.getEventType();
                    while (eventType != XmlPullParser.END_DOCUMENT) {
                        if (eventType == XmlPullParser.START_TAG) {
                            switch (xmlPullParser.getName()) {
                                case "iconback":
                                    for (int i = 0; i < xmlPullParser.getAttributeCount(); i++) {
                                        if (xmlPullParser.getAttributeName(i).startsWith("img")) {
                                            String drawableName = xmlPullParser.getAttributeValue(i);
                                            Bitmap iconBack = loadBitmap(drawableName);
                                            if (iconBack != null) {
                                                backImageList.add(iconBack);
                                            }
                                        }
                                    }
                                    break;
                                case "iconmask":
                                    if (xmlPullParser.getAttributeCount() > 0 && xmlPullParser.getAttributeName(0).equals("img1")) {
                                        String drawableName = xmlPullParser.getAttributeValue(0);
                                        bitmapMaskImage = loadBitmap(drawableName);
                                    }
                                    break;
                                case "iconupon":
                                    if (xmlPullParser.getAttributeCount() > 0 && xmlPullParser.getAttributeName(0).equals("img1")) {
                                        String drawableName = xmlPullParser.getAttributeValue(0);
                                        bitmapFrontImage = loadBitmap(drawableName);
                                    }
                                    break;
                                case "scale":
                                    if (xmlPullParser.getAttributeCount() > 0 && xmlPullParser.getAttributeName(0).equals("factor")) {
                                        mFactor = Float.parseFloat(xmlPullParser.getAttributeValue(0));
                                    }
                                    break;
                                case "item":
                                    String componentName = null;
                                    String drawableName = null;
                                    for (int i = 0; i < xmlPullParser.getAttributeCount(); i++) {
                                        if (xmlPullParser.getAttributeName(i).equals("component")) {
                                            componentName = xmlPullParser.getAttributeValue(i);
                                        } else if (xmlPullParser.getAttributeName(i).equals("drawable")) {
                                            drawableName = xmlPullParser.getAttributeValue(i);
                                        }
                                    }
                                    if (!packagesDrawablesMap.containsKey(componentName)) {
                                        packagesDrawablesMap.put(componentName, drawableName);
                                    }
                                    break;
                                default:
                                    break;
                            }
                        }
                        eventType = xmlPullParser.next();
                    }
                }
                flagLoaded = true;
            } catch (PackageManager.NameNotFoundException e) {
                Logger.e("不能加在图标包:" + e.toString());
            } catch (XmlPullParserException e) {
                Logger.e("无法分析appfilter.xml文件:" + e.toString());
            } catch (IOException e) {
                Logger.e("IOException:" + e.toString());
            }
        }

        @SuppressLint("UseCompatLoadingForDrawables")
        private Bitmap loadBitmap(String drawableName) {
            int id = resourcesIconPacks.getIdentifier(drawableName, "drawable", iconPackPackageName);
            if (id > 0) {
                Drawable bitmap = resourcesIconPacks.getDrawable(id, null);
                if (bitmap instanceof BitmapDrawable) {
                    return ((BitmapDrawable) bitmap).getBitmap();
                }
            }
            return null;
        }

        @SuppressLint("UseCompatLoadingForDrawables")
        private Drawable loadDrawable(String drawableName) {
            int id = resourcesIconPacks.getIdentifier(drawableName, "drawable", iconPackPackageName);
            if (id > 0) {
                return resourcesIconPacks.getDrawable(id, null);
            }
            return null;
        }

        private Bitmap generateBitmap(String appPackageName, Bitmap defaultBitmap) {
            // the key for the cache is the icon pack package name and the app package name
            String key = iconPackPackageName + ":" + appPackageName;

            // if generated bitmaps cache already contains the package name return it
//            Bitmap cachedBitmap = BitmapCache.getInstance(MyApplication.getInstance()).getBitmap(key);
//            if (cachedBitmap != null)
//                return cachedBitmap;

            // if no support images in the icon pack return the bitmap itself
            if (backImageList.size() == 0) {
                return defaultBitmap;
            }

            Random r = new Random();
            int backImageInd = r.nextInt(backImageList.size());
            Bitmap backImage = backImageList.get(backImageInd);
            int w = backImage.getWidth();
            int h = backImage.getHeight();

            // create a bitmap for the result
            Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            Canvas mCanvas = new Canvas(result);

            // draw the background first
            mCanvas.drawBitmap(backImage, 0, 0, null);

            // create a mutable mask bitmap with the same mask
            Bitmap scaledBitmap = defaultBitmap;
            if (defaultBitmap != null && (defaultBitmap.getWidth() > w || defaultBitmap.getHeight() > h)) {
                Bitmap.createScaledBitmap(defaultBitmap, (int) (w * mFactor), (int) (h * mFactor), false);
            }

            if (bitmapMaskImage != null) {
                // draw the scaled bitmap with mask
                Bitmap mutableMask = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
                Canvas maskCanvas = new Canvas(mutableMask);
                maskCanvas.drawBitmap(bitmapMaskImage, 0, 0, new Paint());

                // paint the bitmap with mask into the result
                Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
                mCanvas.drawBitmap(scaledBitmap, (w - scaledBitmap.getWidth()) / 2, (h - scaledBitmap.getHeight()) / 2, null);
                mCanvas.drawBitmap(mutableMask, 0, 0, paint);
                paint.setXfermode(null);
            } else // draw the scaled bitmap without mask
            {
                mCanvas.drawBitmap(scaledBitmap, (w - scaledBitmap.getWidth()) / 2, (h - scaledBitmap.getHeight()) / 2, null);
            }

            // paint the front
            if (bitmapFrontImage != null) {
                mCanvas.drawBitmap(bitmapFrontImage, 0, 0, null);
            }

            // store the bitmap in cache
//            BitmapCache.getInstance(MyApplication.getInstance()).putBitmap(key, result);

            // return it
            return result;
        }

        /**
         * Drawable 转 Bitmap
         *
         * @param drawable Drawable
         * @return Bitmap
         */
        private Bitmap drawableToBitmap(Drawable drawable) {
            Bitmap bitmap;
            int h = drawable.getIntrinsicHeight();
            int w = drawable.getIntrinsicWidth();
            Bitmap.Config config = drawable.getOpacity()
                    != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
            bitmap = Bitmap.createBitmap(w, h, config);
            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, w, h);
            drawable.draw(canvas);
            return bitmap;
        }

        /**
         * Bitmap 转 Drawable
         *
         * @param bitmap Bitmap
         * @return Drawable
         */
        private BitmapDrawable bitmapToDrawable(Bitmap bitmap) {
            return new BitmapDrawable(MyApplication.getInstance().getResources(), bitmap);
        }

        public Drawable getDrawableIconForPackage(String appPackageName, Drawable defaultDrawable) {
            // 加载系统默认图标
            if ("system".equals(iconPackPackageName)) {
                return defaultDrawable;
            }
            // 从图标包选择
            else {
                // 加载用户选定的图标,而不是自动获取图标

                if (!flagLoaded) {
                    load();
                }
                PackageManager packageManager = SoftwareManager.getInstance().getPackageManager();
                Intent intent = packageManager.getLaunchIntentForPackage(appPackageName);
                String componentName = null;
                if (intent != null) {
                    componentName = packageManager.getLaunchIntentForPackage(appPackageName).getComponent().toString();
                }
                String drawable = packagesDrawablesMap.get(componentName);

                // 从图标中第一次查找
                if (drawable != null) {
                    return loadDrawable(drawable);
                }
                // 没找到,加载包名相似的图标
                else {
                    Drawable icon = null;
                    for (String key : packagesDrawablesMap.keySet()) {
                        int start = key.indexOf("{") + 1;
                        int end = key.indexOf("/", start);
                        if (end > start) {
                            String key2 = key.substring(start, end);
                            if (key2.contains(appPackageName)) {
                                drawable = packagesDrawablesMap.get(key);
                                if (drawable != null) {
                                    icon = loadDrawable(drawable);
                                }
                                break;
                            }
                        }

                    }
                    // 第二次加载成功
                    if (icon != null) {
                        return icon;
                    }
                    // 第二次还未加载,自动截取
                    else {
                        // 自动截取
                        if (backImageList.size() > 0) {
                            Bitmap defaultIconBitmap = drawableToBitmap(defaultDrawable);
                            return bitmapToDrawable(generateBitmap(appPackageName, defaultIconBitmap));
                        } else {
                            return defaultDrawable;
                        }
                    }
                }
            }
        }

        public Bitmap getIconForPackage(String appPackageName, Bitmap defaultBitmap) {
            if (!flagLoaded) {
                load();
            }

            PackageManager pm = MyApplication.getInstance().getPackageManager();
            Intent launchIntent = pm.getLaunchIntentForPackage(appPackageName);
            String componentName = null;
            if (launchIntent != null) {
                componentName = pm.getLaunchIntentForPackage(appPackageName).getComponent().toString();
            }
            String drawable = packagesDrawablesMap.get(componentName);
            if (drawable != null) {
                return loadBitmap(drawable);
            } else {
                // try to get a resource with the component filename
                if (componentName != null) {
                    int start = componentName.indexOf("{") + 1;
                    int end = componentName.indexOf("}", start);
                    if (end > start) {
                        drawable = componentName.substring(start, end).toLowerCase(Locale.getDefault()).replace(".", "_").replace("/", "_");
                        if (resourcesIconPacks.getIdentifier(drawable, "drawable", iconPackPackageName) > 0) {
                            return loadBitmap(drawable);
                        }
                    }
                }
            }
            return generateBitmap(appPackageName, defaultBitmap);
        }
    }
}
